#ifndef _CRE_BREAKABLE_GLASS_BUFFER_
#define _CRE_BREAKABLE_GLASS_BUFFER_
#pragma once

#include "CREBreakableGlassConfig.h"

//==================================================================================================
// Name: CREBreakableGlassBuffer
// Desc: Breakable glass geometry buffer management
// Author: Chris Bunner
//==================================================================================================
class CREBreakableGlassBuffer
{
public:
	CREBreakableGlassBuffer();
	~CREBreakableGlassBuffer();

	// Singleton access from Render Thread
	static CREBreakableGlassBuffer& RT_Instance();
	static void RT_ReleaseInstance();

	// Buffer allocation
	uint32	RT_AcquireBuffer(const bool isFrag = false);
	bool		RT_IsBufferValid(const uint32 id, const bool isFrag = false) const;

	// Buffer updates
	bool		RT_UpdatePlaneVertexBuffer(const uint32 id, PodArray<SVF_P3F_C4B_T2F>& vertices, const bool isFrag);
	bool		RT_UpdatePlaneIndexBuffer(const uint32 id, PodArray<uint16>& indices, const bool isFrag);
	bool		RT_UpdatePlaneTangentBuffer(const uint32 id, PodArray<SPipTangents>& tangents, const bool isFrag);

	bool		RT_UpdateCrackVertexBuffer(const uint32 id, PodArray<SVF_P3F_C4B_T2F>& vertices, const bool isFrag);
	bool		RT_UpdateCrackIndexBuffer(const uint32 id, PodArray<uint16>& indices, const bool isFrag);
	bool		RT_UpdateCrackTangentBuffer(const uint32 id, PodArray<SPipTangents>& tangents, const bool isFrag);

	bool		RT_ClearBuffer(const uint32 id, const bool isFrag);

	// Buffer drawing
	bool		RT_DrawPlaneBuffer(const uint32 id, const bool isFrag);
	bool		RT_DrawCrackBuffer(const uint32 id, const bool isFrag);

	// Buffer state
	static const uint32	NoBuffer = 0;
	static const int		InvalidStream = -1;

	enum EBufferType
	{
		EBufferType_Solid = 0,
		EBufferType_Loose,
		EBufferType_Num
	};
	
private:
	// Internal allocation
	void		InitialiseBuffers();
	void		ReleaseBuffers();

	// Internal updates
	bool		UpdateVBuffer(const uint32 id, SVF_P3F_C4B_T2F* pVertData, const uint32 vertCount, const uint32 offset, const bool isFrag);
	bool		UpdateIBuffer(const uint32 id, uint16* pIndData, const uint32 indCount, const uint32 offset, const bool isFrag);
	bool		UpdateTBuffer(const uint32 id, SPipTangents* pTanData, const uint32 tanCount, const uint32 offset, const bool isFrag);

	// Internal drawing
	void		DrawBuffer(const uint32 count, const uint32 offset, const uint32 cyclicId, const bool isFrag);

	// Common buffer state/data
	struct SBuffer
	{
		SBuffer()
		: pVertices(NULL)
		, pIndices(NULL)
		, tangentStreamID(InvalidStream)
		, pLastCrackIndCount(NULL)
		, pLastPlaneIndCount(NULL)
		, nextId(10) // NumBufferSlots
		{
		}

		// Geometry buffers
		CVertexBuffer*	pVertices;
		CIndexBuffer*		pIndices;
		int							tangentStreamID;
		
		// State	
		uint32*					pLastPlaneIndCount;
		uint32*					pLastCrackIndCount;
		uint32					nextId;
	};

	// Buffer data
	ILINE bool IsVBufferValid(const SBuffer& buffer) const
	{
		return buffer.pVertices && buffer.pIndices && buffer.tangentStreamID != InvalidStream;
	}

	ILINE const SBuffer& GetBuffer(const bool isFrag) const
	{
		const EBufferType buffType = isFrag ? EBufferType_Loose : EBufferType_Solid;
		return m_buffer[buffType];
	}

	SBuffer					m_buffer[EBufferType_Num];

	// Singleton instance
	static CREBreakableGlassBuffer* s_pInstance;
};

#endif // _CRE_BREAKABLE_GLASS_BUFFER_